/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.system.auth.autoconfigure;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import me.ijleex.mgmt.system.auth.filter.JwtAuthenticationFilter;
import me.ijleex.mgmt.system.auth.handler.EntryPointUnauthorizedHandler;
import me.ijleex.mgmt.system.auth.handler.RestAccessDeniedHandler;
import me.ijleex.mgmt.system.auth.service.impl.JwtUserServiceImpl;

/**
 * 安全配置.
 *
 * @author liym
 * @since 2020-08-09 10:48 新建
 */
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private static final String[] ALLOW_GET_RESOURCES = {"/**/*.html", "/**/*.htm", "/**/*.css", "/**/*.js",
            "/**/*.xhtml", "/**/*.woff", "/**/*.woff2", "/**/*.ttf",
            "/**/*.tif", "/**/*.tiff", "/**/*.svg", "/**/*.png",
            "/**/*.jpg", "/**/*.jpeg", "/**/*.gif", "/**/*.ico",
            "/**/*.webp", "/**/*.swf", "/**/*.pdf", "/**/*.mp3",
            "/**/*.wav", "/**/*.avi"};

    private final String[] postAllowPatterns;

    private final JwtUserServiceImpl jwtUserService;

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
    private final RestAccessDeniedHandler restAccessDeniedHandler;

    public WebSecurityConfiguration(AuthProperties properties, JwtUserServiceImpl jwtUserService, JwtAuthenticationFilter jwtAuthenticationFilter, EntryPointUnauthorizedHandler entryPointUnauthorizedHandler, RestAccessDeniedHandler restAccessDeniedHandler) {
        this.postAllowPatterns = properties.getPostAllowPatterns().toArray(new String[0]);

        this.jwtUserService = jwtUserService;

        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
        this.entryPointUnauthorizedHandler = entryPointUnauthorizedHandler;
        this.restAccessDeniedHandler = restAccessDeniedHandler;
    }

    @Bean(name = "authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.jwtUserService).passwordEncoder(this.passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().cors()
                .and().authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers(HttpMethod.GET, "/").permitAll()
                .antMatchers(HttpMethod.GET, ALLOW_GET_RESOURCES).permitAll()
                .antMatchers(HttpMethod.POST, this.postAllowPatterns).permitAll()
                .anyRequest().authenticated()
                .and().headers().cacheControl();
        http.exceptionHandling().authenticationEntryPoint(this.entryPointUnauthorizedHandler).accessDeniedHandler(this.restAccessDeniedHandler);
        http.addFilterBefore(this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

}
